#include "RtsConnection.h"

// Debug:
#include <iostream>
using std::cout;
using std::endl;

/*
	Implementation of basic passing of data with
	RPC client<->server. 
*/


namespace Putin{

RtsConnection::RtsConnection(void)
{
	mIsClient = false;

}

RtsConnection::~RtsConnection(void)
{
}

// -------------------------
// Implementation of RPCs
// Note: All RPC are in Guaranteed order, which is prob. needed for the sync to work.
// However, that also potentially reduces speed a bit..
TNL_IMPLEMENT_NETCONNECTION(RtsConnection, TNL::NetClassGroupGame, true);

TNL_IMPLEMENT_RPC(	RtsConnection, rpcCSText, 
					(U16 id1, U16 id2, StringPtr text), (id1,id2,text),
					TNL::NetClassGroupGameMask, TNL::RPCGuaranteedOrdered, TNL::RPCDirClientToServer,0)
{
	NetworkText* nt = new NetworkText(id1,  id2, std::string( (const char*)text ));
	for each( NetFunctorB* f in getMainConn()->mHandlers)
		(*f)(this, nt);
}

TNL_IMPLEMENT_RPC(	RtsConnection, rpcCSCommand, 
					(U16 id1, U16 id2, U16 command, F32 x, F32 y), (id1,id2,command,x,y),
					TNL::NetClassGroupGameMask, TNL::RPCGuaranteedOrdered, TNL::RPCDirClientToServer,0)
{
	NetworkCommand* nc = new NetworkCommand(command, id1,  id2 , x, y);
	for each( NetFunctorB* f in getMainConn()->mHandlers)
		(*f)(this, nc);
}

TNL_IMPLEMENT_RPC(	RtsConnection, rpcSCText, 
					(U16 id1, U16 id2, StringPtr text), (id1,id2,text),
					TNL::NetClassGroupGameMask, TNL::RPCGuaranteedOrdered, TNL::RPCDirServerToClient,0)
{
	NetworkText* nt = new NetworkText(id1,  id2, std::string( (const char*)text ));
	for each( NetFunctorB* f in getMainConn()->mHandlers)
		(*f)(this, nt);
}

TNL_IMPLEMENT_RPC(	RtsConnection, rpcSCCommand, 
					(U16 id1,U16 id2, U16 command, F32 x, F32 y), (id1,id2,command,x,y),
					TNL::NetClassGroupGameMask, TNL::RPCGuaranteedOrdered, TNL::RPCDirServerToClient,0)
{
	NetworkCommand* nc = new NetworkCommand(command,id1,  id2, x, y);
	for each( NetFunctorB* f in getMainConn()->mHandlers)
		(*f)(this, nc);
}
//----------------------------------
// IMPLEMENT NetworkConnection 


void RtsConnection::broadcast(NetworkPacket* np, bool skipEcho)
{
	RtsConnection* c = getMainConn();
	TNL::Vector<TNL::NetConnection*> conns = c->mInterface->getConnectionList();
	
	for(int i=0;i<conns.size();i++)
	{
		RtsConnection* con = dynamic_cast<RtsConnection*>(conns[i]);
		if( skipEcho && this==con) // Do not broadcast back to this connection.
			continue;

		// What packet type is this? Make sure the conversion is right done.
		// TODO: Can we use dynamic_cast and RTTI? 
		switch(np->getType()){
			case NetworkPacket::npText:
			{
				//cout << "broadcasting text" << endl;
				NetworkText* nt = dynamic_cast<NetworkText*>(np);
				con->rpcSCText(nt->id1,nt->id2, TNL::StringPtr(nt->text.c_str()));
				break;
			}
			case NetworkPacket::npCommand:
			{
				//cout << "broadcasting cmd" << endl;
				NetworkCommand* nc = dynamic_cast<NetworkCommand*>(np);
				con->rpcSCCommand(nc->id1,nc->id2,nc->command,nc->x,nc->y);
				break;
			}
		}
	}
}

void RtsConnection::send2Client(NetworkPacket* np)
{
	switch(np->getType()){
		case NetworkPacket::npText:
		{
			//cout << "sending text 2 client" << endl;
			NetworkText* nt = reinterpret_cast<NetworkText*>(np);
			rpcSCText(nt->id1,nt->id2, TNL::StringPtr(nt->text.c_str()));
			break;
		}
		case NetworkPacket::npCommand:
		{
			//cout << "sending cmd 2 client" << endl;
			NetworkCommand* nc = reinterpret_cast<NetworkCommand*>(np);
			rpcSCCommand(nc->id1,nc->id2,nc->command,nc->x,nc->y);
			break;
		}
	}
}

void RtsConnection::send2Server(NetworkPacket* np)
{
	switch(np->getType()){
		case NetworkPacket::npText:
		{
			//cout << "sending cmd 2 server" << endl;
			NetworkText* nt = reinterpret_cast<NetworkText*>(np);
			rpcCSText(nt->id1,nt->id2, TNL::StringPtr(nt->text.c_str()));
			break;
		}
		case NetworkPacket::npCommand:
		{
			//cout << "sending cmd 2 server" << endl;
			NetworkCommand* nc = reinterpret_cast<NetworkCommand*>(np);
			rpcCSCommand(nc->id1,nc->id2,nc->command,nc->x,nc->y);
			break;
		}
	}
}

void RtsConnection::addHandler(NetFunctorB* nf)
{
	mHandlers.push_back(nf);
}

void RtsConnection::addOnConnectHandler( NetFunctorB* nf)
{
	mConnectHandlers.push_back(nf);
}

void RtsConnection::addOnDisconnectedHandler(NetFunctorB* nf)
{
	mDisconnectedHandlers.push_back(nf);
}

std::string RtsConnection::getRemoteAddress()
{
	TNL::Address ad = getNetAddress();
	return ad.toString();
}

// ===== END IMPLEMENTATION OF NetworkConnection

bool RtsConnection::startClient(TNL::Address address)
{
	mIsClient = true;
	TNL::Address bindAddress(TNL::IPProtocol, TNL::Address::Any,0);
	mInterface = new TNL::NetInterface(bindAddress);
	connect(mInterface,address);
	return true;
}

bool RtsConnection::startServer(TNL::Address address)
{
	mInterface = new TNL::NetInterface(address);
	mInterface->setAllowsConnections(true);
	RtsConnection::mMainServerConn = this;
	return true;
}

void RtsConnection::checkMessages()
{
	mInterface->checkIncomingPackets();
	mInterface->processConnections();
}

void RtsConnection::onConnectionEstablished()
{
	for each( NetFunctorB* f in getMainConn()->mConnectHandlers)
		(*f)(this,NULL);
}

void RtsConnection::onConnectionTerminated(TNL::NetConnection::TerminationReason tr, const char* errorStr)
{
	for each(NetFunctorB* f in getMainConn()->mDisconnectedHandlers)
		(*f)(this,NULL);
}

void RtsConnection::onConnectTerminated(TNL::NetConnection::TerminationReason tr, const char* rejectionStr)
{
	for each(NetFunctorB* f in getMainConn()->mDisconnectedHandlers)
		(*f)(this,NULL);
}


RtsConnection* RtsConnection::getMainConn()
{
	return  mIsClient ? this : mMainServerConn;
}

RtsConnection* RtsConnection::mMainServerConn = NULL;
}